The CALL_EXTERNAL Function

IDL allows you to integrate programs written in other languages with your IDL code, either by calling a compiled function from an IDL program or by linking a compiled function into IDL’s internal system routine table:

This topic covers the basics of using CALL_EXTERNAL from IDL, then discusses platform-specific options for the UNIX and Windows versions of IDL. It can be helpful to refer to the documentation for CALL_EXTERNAL when reading this material.

The CALL_EXTERNAL function loads and calls routines contained in shareable object libraries. Arguments passed to IDL are passed to this external code, and returned data from the external code is automatically presented as the result from CALL_EXTERNAL as an IDL variable. IDL and the called routine share the same process address space. Because of this, CALL_EXTERNAL avoids the overhead of process creation of the SPAWN routine. In addition, the shareable object library is only loaded the first time it is referenced, saving overhead on subsequent calls.

CALL_EXTERNAL is much easier to use than writing a system routine. Unlike a system routine, however, CALL_EXTERNAL does not check the type or number of parameters. Programming errors in the external routine are likely to result in corrupted data (either in the routine or in IDL) or to cause IDL to crash. See Common CALL_EXTERNAL Pitfalls for help in avoiding some of the more common mistakes.

Example Code in the IDL Distribution

This section contains examples of CALL_EXTERNAL use. All of the code for these examples, along with additional examples, can be found in the call_external subdirectory of the external directory of the IDL distribution. The C language examples use the MAKE_DLL procedure, and can therefore be easily run on any platform supported by IDL. To build the sharable library containing the external C code and then run all of the provided examples, execute the following IDL statements:

PUSHD, FILEPATH(’’,SUBDIRECTORY=[’external’,’call_external’,’C’]) ALL_CALLEXT_EXAMPLES

POPD

Additional information on these examples, including details on running the individual examples, can be found in the README file located in that directory.

CALL_EXTERNAL Compared to UNIX Child Process

In many situations, a UNIX IDL user has a choice of using the SPAWN procedure to start a child process that executes external code and communicates with IDL via a pipe connecting the two processes. The advantages of this approach are:

The advantages of CALL_EXTERNAL are:

Compilation and Linking of External Code

Each operating system requires different compilation and link statements for producing a shareable object suitable for usage with CALL_EXTERNAL. This is even true between different implementations of a common operating system family. For example, most UNIX systems require unique options despite their shared heritage. You must consult your system and compiler documentation to find the appropriate options for your system.

The IDL MAKE_DLL procedure provides a portable, high-level mechanism for building sharable libraries from code written in the C language. In many situations, this procedure can completely handle the task of building sharable libraries to be used with CALL_EXTERNAL. MAKE_DLL requires that you have a C compiler installed on your system that is compatible with the compiler described by the IDL !MAKE_DLL system variable.

The !MAKE_DLL system variable is used by the MAKE_DLL procedure to construct C compiler and linker commands appropriate for the target platform. If you do not use MAKE_DLL to compile and link your code, you may find the contents of !MAKE_DLL.CC and !MAKE_DLL.LD helpful in determining which options to specify to your compiler and linker, in conjunction with your system and compiler documentation. For the C language, the options in !MAKE_DLL should be very close to what you need. For other languages, the !MAKE_DLL options should be helpful in determining which options to use, as on most systems, all the language compilers accept similar options.

AUTO_GLUE

As described in Passing Parameters , CALL_EXTERNAL uses the IDL Portable Calling Convention to call external code. This convention uses an (argc, argv) style interface to allow CALL_EXTERNAL to call routines with arbitrary numbers and types of arguments. Such an interface is necessary, because IDL, like any compiled program, cannot generate arbitrary function calls at runtime.

Most C functions are not written to the IDL portable convention. Rather, they are written using the natural form of argument passing used in compiled programs. It is therefore common for IDL programmers to write so-called glue functions to match the IDL calling interface to that of the target function. On systems that have a C compiler installed that is compatible with the one described by the IDL !MAKE_DLL system variable, the AUTO_GLUE keyword to CALL_EXTERNAL can be used to instruct IDL to automatically write, compile, and load this glue code on demand, and using a cache to preserve this glue code for future invocations of functions with the same interface.

AUTO_GLUE thus allows CALL_EXTERNAL to call functions with a natural interface, without requiring the user to write or compile additional code. AUTO_GLUE is described in the documentation for “CALL_EXTERNAL” , as well as in Using Auto Glue. The examples given in Basic C Examples show CALL_EXTERNAL used with and without AUTO_GLUE.

Input and Output

Input and output actions should be performed within IDL code, using IDL’s built-in input/output facilities, or by using IDL_Message(). Performing input/output from code external to IDL, especially to the user console or tty (e.g. stdin or stdout), may generate unexpected results.

Memory Cleanup

IDL has a strict internal policy that it does not perform memory cleanup on memory it did not allocate. This is necessary so that external code which allocates memory can use any memory allocation package it desires; with this way, there is no confusion about which code is responsible for releasing allocated memory.

The code that allocates memory is always responsible for freeing it. IDL allocates and frees memory for its internal needs, and external code is not allowed to release such memory except through a proper IDL function documented for that purpose. Similarly, IDL will not intentionally free memory that it did not allocate. As such, IDL does not perform any memory cleanup calls on the values returned from external code called via the CALL_EXTERNAL routine. Because of this, any dynamic memory returned to IDL will not be returned to the system, which will result in a memory leak. Be sure to design CALL_EXTERNAL routines in a way as not to return dynamically allocated memory to IDL. Passing String Data provides an example of doing this with strings.

Memory Access

IDL and your external code share the same address space within the same running program. This means that mistakes common in compiled languages, such as a wild pointer altering memory that it does not own, can cause problems elsewhere. In particular, external code can easily corrupt IDL’s data structures and otherwise cause IDL to fail. Authors of such code must be especially careful to guard against such errors.

Argument Data Types

When using CALL_EXTERNAL to call external code, IDL passes its arguments to the called code using the data types that were passed to it. It has no way to verify independently that these types are the actual types expected by the external routine. If the data types passed are not of the types expected by the external code, the results are undefined, and can easily include memory corruption or even crashing of the IDL program.

Note: You must ensure that the arguments passed to external code are of the exact type expected by that routine. Failure to do so will result in undefined behavior.

Mapping IDL Data Types to External Language Types

When writing external code for use with CALL_EXTERNAL, your code must use data types that are compatible with the C data types used internally by IDL to represent the IDL data types. This mapping is covered in the topic IDL Internals: Types.

By-Value and By-Reference Arguments

There are two basic forms in which arguments can be passed between functions in compiled languages such as C/C++ and Fortran. To use CALL_EXTERNAL successfully, you should be comfortable with these terms and their meanings. In particular, Fortran programmers are often unaware that Fortran code passes everything by reference, and that C code defaults to passing everything by value.

By default, CALL_EXTERNAL passes arguments by reference (unless this behavior is explicitly altered by the use of the ALL_VALUE or VALUE keywords), so no special action is typically required to call Fortran code via CALL_EXTERNAL.

Note: You must ensure that the arguments passed to external code are passed using the correct method: by value, or by reference. Failure to do so will result in undefined behavior.

Arguments Passed by Value

A copy of the value of the argument is passed to the called routine. Any changes made to such a value by the called routine are local to that routine, and do not change the original value of the variable in the calling routine. C/C++ pass everything by value, but have an explicit address-of operator (&) that is used to pass addresses of variables and get by-reference behavior.

Arguments Passed by Reference

The machine address of the argument is passed to the called routine. Any changes made to such a value by the called routine are immediately visible to the caller, because both routines are actually modifying the same memory addresses. Fortran passes everything by reference, but most Fortran implementations support intrinsic operators that allow the programmer control over this (sometimes called %LOC and %VAL, or just LOC and VAL). Consult your compiler documentation for details.

Microsoft Windows Calling Conventions

All operating system/hardware combinations define an inter-routine calling convention. A calling convention defines the rules used for passing arguments between routines, and specifies such details as how arguments of different types are passed (i.e. in registers or on the system stack) and how and when such arguments are cleaned up.

A stable and efficient calling convention is critical to the stability of an operating system, and can affect most aspects of the system:

Microsoft Windows is unique among the platforms supported by IDL in that it has two distinct calling conventions in common use, whereas other systems define a single convention. On single-convention systems, the calling convention is unimportant to application programmers; it is of concern only to hardware designers, authors of compilers, and operating systems. On a multiple convention system, application programmers need to ensure that their code is compiled to use the proper convention and that calls to that code use the same convention. The Microsoft Calling Conventions are described below.

STDCALL

STDCALL is the calling convention used by the majority of the Windows operating system API. In a STDCALL call, the calling routine places the arguments in the proper registers and/or stack locations, and the called routine is responsible for cleaning them up and unwinding the stack.

CDECL

CDECL is the calling convention used by C/C++ code by default. This default can be changed via compiler switches, declspec declarations, or #pragmas. With CDECL, the caller is responsible for both setup and cleanup of the arguments. CDECL is able to call functions with variable numbers of arguments (varargs functions) because the caller knows the actual number of arguments passed at runtime, whereas STDCALL cannot call such functions. This is because the STDARGS routine cannot know efficiently at compile time how many arguments it will be passed at runtime in these situations.

The inconvenience of having two distinct and incompatible calling conventions is usually minor, because the header files that define functions for C/C++ programs include the necessary definitions such that the compiler knows to generate the proper code to call them and the programmer is not required to be aware of the issue. However, CALL_EXTERNAL does have a problem: Unlike a C/C++ program, IDL determines how to call a function solely by the arguments passed to CALL_EXTERNAL, and not from a header file.

IDL therefore has no way to know how your external code was compiled. It uses the STDARG convention by default, and the CDECL keyword can be used to change the default. CALL_EXTERNAL therefore relies on the IDL user to tell it which convention to use. If IDL calls your code using the correct convention, it will work correctly. If it calls using the wrong convention, the results are undefined, including memory corruption and possible crashing of the IDL program.

The default calling convention for CALL_EXTERNAL is STDCALL, whereas the default convention for the Microsoft C compiler is CDECL. Hence, Windows users must usually specify the CDECL keyword when calling such code from IDL. Non- Windows versions of IDL ignore the CDECL keyword, so it is safe to always include it in cross platform code.

Here is what happens when external code is called via the wrong calling convention:

Either combination is bad, and can corrupt or kill the program. Sometimes this happens, and sometimes it doesn’t, so the results can be random and mysterious to programmers who are not aware of the issue.

When the wrong calling convention is used, the process stack can become confused. A “smashed stack” visible from the C debugger following a CALL_EXTERNAL is usually indicative of when the wrong calling convention was used.

Common CALL_EXTERNAL Pitfalls

Following are a list of common errors and mistakes commonly seen when using CALL_EXTERNAL.

See Also

AUTO_GLUE, CALL_EXTERNAL, MAKE_DLL, SPAWN, Using SPAWN and Pipes, !MAKE_DLL, Using AUTO_GLUE,